-
-
Notifications
You must be signed in to change notification settings - Fork 5
feat: support Synced Queries #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
f6fa84a
to
e5137fc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@danielroe good to merge? |
apologies for the delay. I want to review with an eye to how we would implement in nuxt (where provide/inject is often not the best pattern). ideally something that would support lazy creation of the stateful singleton (ie on first access) |
I'm doing this. A global variable and checking if it exist on each access. I watch the user session but most of the time changing the value comes with navigation/reload so not sure how necessary it is. import { Zero } from "@rocicorp/zero";
import { schema, clientMutators } from "~~/zero";
import type { AuthData } from "~~/zero";
import { decodeJwt } from "jose";
let client: Zero<typeof schema, ReturnType<typeof clientMutators>>;
export function useZero() {
const { session } = useUserSession();
const config = useRuntimeConfig().public.zero;
const decodedJWT = computed<AuthData | undefined>(() => session.value?.token ? decodeJwt<AuthData>(session.value.token) : undefined);
const userID = computed(() => decodedJWT.value?.sub ?? 'anon');
watch([userID], () => {
if (client && client.userID === userID.value)
return;
if (client && !client.closed) {
client.close();
}
client = new Zero({
userID: userID.value,
auth: () => session.value?.token,
mutators: clientMutators(decodedJWT.value),
server: import.meta.client ? config.server : undefined,
schema,
});
}, { immediate: true });
return client;
} |
@danielroe would doing something along the lines of what Pinia does: export let activeZero: Zero<any, any> | undefined
export function setActiveZero(zero: Zero<any, any>) {
activeZero = zero
}
export function useZero() {
return ((hasInjectionContext() && inject(zeroSymbol)) || shallowRef(activeZero))
} in combination with the plugin proposed in this PR and a to-be-created Nuxt module be a step in the right direction? While not lazy-creation, it would make sure that Zero is available during SSR. You also keep the benefit of Although I need to add that I don't have a lot of experience with Nuxt, so if you know of any libraries that have a more 'nuxt-native' implementation that would be very welcome. |
47bcfea
to
ccdf981
Compare
@danielroe could you put in a review? |
No dependency changes detected. Learn more about Socket for GitHub. 👍 No dependency changes detected in pull request |
The problem I have when looking at this solution is that I cannot run multiple zero sessions side by side (something I'm doing atm). Can you think of a way that provides the instance a different way? Right now that won't work What I tried for example: export function createZero() {
let zero;
function useZero() {
// init & watch options etc...
return zero
}
function useQuery() {
// wrap the useQuery composable and pass the zero instance
}
return {
useZero,
useQuery,
}
} And then the user can create their own instance composable: export { useZero, useQuery } = createZero<Schema, Mutators>() Not sure about the UX though |
@Gerbuuun This is very interesting, seems like the way to go.
I think this would actually be better, since this PR already introduces the I'm not sure how well this can be adapted to work in Nuxt, @Gerbuuun are you able to weigh in on that as well? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This createZero
function is now a composable. This means it does not work out of the vue context. z
is not shared and every time you need to call createZero
.
What I meant with my example is to call createZero
top-level which then creates the composables useZero
and useQuery
. That way each time you call the imported composable useZero
, you get the already existing instance.
I haven't fully worked it out from there because of reactivity problems. I'll clean up my code and share the branch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant with my example is to call createZero top-level which then creates the composables useZero and useQuery
These changes achieve exactly that right? The concept is that you call createZero
once for each zero instance/session you have in your application. The useZero
and useQuery
composables can then be exposed to the rest of the application.
This createZero function is now a composable. This means it does not work out of the vue context.
Could you elaborate what doesn't work outside of the vue context?
I have created a draft PR #137 with my idea and a working playground so you can check it out @maxstevens-nl Please let me know what you think. |
This PR adds support for Synced Queries in a backwards compatible way. This is done by introducing a new
createZero
composable, which manages the lifecycle of a zero instance, and returnsuseZero
anduseQuery
composables.To maintain backwards compatibility,
useQuery
is still exported but marked deprecated.